package com.example.springsecuritydemo.utils;


import com.example.springsecuritydemo.costant.ResultCode;
import com.example.springsecuritydemo.exception.JWTTokenException;
import com.example.springsecuritydemo.support.UserContext;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * jwt工具类
 *
 * @author chenggang
 * @date 2019/04/09
 */
@Component
public class JWTTokenUtil {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.access_token.expiration}")
    private Long accessTokenExpiration;

    private static final String AES_PASSWORD = "dong9@leke";

    private static final String CLAIM_KEY_USERNAME = "sub";

    private static final String CLAIM_KEY_ID = "id";

    private static final String CLAIM_KEY_CREATED = "created";

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 从令牌中获取用账号
     *
     * @param token 令牌
     * @return 用户名
     */
    public String getUserAccountFromToken(String token) {
        String userAccount = null;
        try {
            Claims claims = getClaimsFromToken(token);
            userAccount = AESUtil.decode(claims.getSubject(), AES_PASSWORD);
        } catch (Exception e) {
            logger.error("获取用户账号出错了,token: " + token, e);
            throw new JWTTokenException(ResultCode.NOT_LOGIN.getCode(), "解析usrname失败");
        }
        return userAccount;
    }

    public Date getCreatedDateFromToken(String token) {
        Date created = null;
        try {
            final Claims claims = getClaimsFromToken(token);
            if (null != claims) {
                created = new Date((Long) claims.get(CLAIM_KEY_CREATED));
            }
        } catch (Exception e) {
            logger.error("jwt解析created出错了", e);
        }
        return created;
    }

    public Date getExpirationDateFromToken(String token) {
        Date expiration = null;
        try {
            final Claims claims = getClaimsFromToken(token);
            if (null != claims) {
                expiration = claims.getExpiration();
            }
        } catch (Exception e) {
            logger.error("jwt解析Expiration出错了", e);
        }
        return expiration;
    }

    /**
     * 生成过期时间
     *
     * @param expiration
     * @return
     */
    private Date generateExpirationDate(long expiration) {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }

    /**
     * 从token中获取用户Id
     *
     * @return
     */
    private Integer getUserIdDateFromToken(String token) {
        try {
            Integer userId = null;
            final Claims claims = getClaimsFromToken(token);
            if (null != claims) {
                String id = (String) claims.get(CLAIM_KEY_ID);
                String encodeUserId = AESUtil.decode(id, AES_PASSWORD);
                if (StringUtils.isNumeric(encodeUserId)) {
                    userId = Integer.valueOf(encodeUserId);
                }
            }
            return userId;
        } catch (Exception e) {
            logger.error("jwt解析用户id出错了", e);
            throw new JWTTokenException(ResultCode.NOT_LOGIN.getCode(), "解析用户ID失败");
        }
    }

    /**
     * 生成令牌
     *
     * @param user 用户
     * @return 令牌
     */
    public String generateToken(UserContext.User user) {
        Map<String, Object> claims = new HashMap<>();
        //对token中的userId和userName进行加密
        claims.put(CLAIM_KEY_USERNAME, AESUtil.encode(user.getUserAccount(), AES_PASSWORD));
        claims.put(CLAIM_KEY_CREATED, new Date());
        claims.put(CLAIM_KEY_ID, AESUtil.encode(user.getUserId() + "", AES_PASSWORD));
        return generateToken(claims);
    }

    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    public String generateToken(Map<String, Object> claims) {
        Date expirationDate = generateExpirationDate(accessTokenExpiration);
        return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    private Claims getClaimsFromToken(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            logger.error("解析令牌出错了,token: " + token, e);
            throw new JWTTokenException(ResultCode.NOT_LOGIN.getCode(), "解析token失败");
        }
        return claims;
    }

    /**
     * 判断令牌是否过期
     *
     * @param token 令牌
     * @return 是否过期
     */
    public Boolean isTokenExpired(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            Date expiration = claims.getExpiration();
            return expiration.before(new Date());
        } catch (Exception e) {
            logger.error("判断令牌是否过期,token: " + token, e);
            throw new JWTTokenException(ResultCode.NOT_LOGIN.getCode(), "token已经过期");
        }
    }


    /**
     * 校验token
     *
     * @param token
     * @param user
     * @return
     */
    public Boolean validateToken(String token, UserContext.User user) {
        final String userAccount = getUserAccountFromToken(token);
        final Integer userId = getUserIdDateFromToken(token);
        if (userAccount.equals(user.getUserAccount()) && !isTokenExpired(token) && user.getUserId().equals(userId)) {
            return true;
        }
        return false;
    }

}
